/**
 * 接口是一系列抽象方法的声明，是一些方法特征的集合，这些方法都应该是抽象的，
 * 需要由具体的类去实现，然后第三方就可以通过这组抽象方法调用，让具体的类执行具体的方法。
 */

/*
以下实例中，我们定义了一个接口 IPerson，接着定义了一个变量 customer，它的类型是 IPerson。
customer 实现了接口 IPerson 的属性和方法。
建议接口的名称加上 I 前缀。
*/

interface IPerson {
  firstName: string;
  lastName: string;
  sayHi: () => string;
}

var customer: IPerson = {
  firstName: "Tom",
  lastName: "Hanks",
  sayHi: (): string => "Hi there",
  //aa:20 不能私自添加成员
};
console.log("Customer 对象 ");
console.log(customer.firstName);
//需要注意接口不能转换为 JavaScript。 它只是 TypeScript 的一部分。

//接口中的联合类型
interface RunOptions {
  age?: number; //age代表是可选的成员
  program: string;
  // commandline  可以是字符串数组 可以是字符串 也可以是返回值类型为字符串的函数
  commandline: string[] | string | (() => string);
}

// commandline 是字符串
var options: RunOptions = { program: "test1", commandline: "Hello" };
console.log(options.commandline);

// commandline 是字符串数组
options = { program: "test1", commandline: ["Hello", "World"] };
console.log(options.commandline[0]);
console.log(options.commandline[1]);

// commandline 是一个函数表达式
options = {
  program: "test1",
  commandline: () => {
    //也可以省略设置返回值类型
    return "**Hello World**";
  },
};
var fn: any = options.commandline;
console.log(fn());

/**
 * 任意属性
 * 有时候我们希望一个接口允许有任意的属性，可以使用如下方式：
 */

interface Person {
  name: string;
  age?: number;
  [propName: string]: any; //使用 [propName: string] 定义了任意属性取 string 类型的值。
}

let tom: Person = {
  name: "Tom",
  gender: "male",
  abc: 20,
};
console.log(tom);

/**
 * 需要注意的是，一旦定义了任意属性，那么确定属性和可选属性的类型都必须是它的类型的子集：
 */
interface Person1 {
  name: string;
  age: number;
  [propName: string]: string;
}

let tom1: Person1 = {
  name: "Tom",
  age: 25,
  gender: "male",
};
//报错原因：number不是string的子集

//一个接口中只能定义一个任意属性。如果接口中有多个类型的属性，则可以在任意属性中使用联合类型：
interface Person2 {
  name: string;
  age?: number;
  [propName: string]: string | number;
}
let tom3: Person2 = {
  name: "Tom",
  age: 25,
  gender: "male",
  qaz: 80,
};

/**
 * 只读属性
 * 有时候我们希望对象中的一些字段只能在创建的时候被赋值，那么可以用 readonly 定义只读属性：
 */

interface Person4 {
  readonly id: number;
  name: string;
  age?: number;
  [propName: number]: any; //任意类型的key只能为数字
}

let tom4: Person4 = {
  id: 89757,
  name: "Tom",
  0: "male",
};

tom4.id = 9527;

/**
 * 接口继承
 * 接口继承就是说接口可以通过其他接口来扩展自己。
 * Typescript 允许接口继承多个接口。
 * 继承使用关键字 extends。
 */

//单接口继承语法格式
interface Person5 {
  age: number;
}
interface Musician extends Person5 {
  instrument: string;
}
var drummer = <Musician>{};
drummer.age = 27;
drummer.instrument = "Drums";
console.log("年龄:  " + drummer.age);
console.log("喜欢的乐器:  " + drummer.instrument);

//多继承实例
interface IParent1 {
  v1: number;
}
interface IParent2 {
  v2: number;
}
interface Child extends IParent1, IParent2 {}
var Iobj: Child = { v1: 12, v2: 23 };
console.log("value 1: " + Iobj.v1 + " value 2: " + Iobj.v2);
